package app.sys.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import app.sys.global.ApplicationContextHelper;
import app.sys.model.DelInsertModel;
import app.sys.model.RetCode;
import app.sys.model.SearchCondition;
import app.sys.mybatis.CustomMapWrapper;
import app.sys.model.ResponseObj;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 增删改差基础Controller
 *
 * @param <S> Service类型
 * @param <M> Mapper类型
 * @param <T> 增删改差的对象类型
 * @param <I> id的类型
 */
public abstract class BaseRestController<S extends ServiceImpl<M, T>, M extends BaseMapper<T>, T, I extends Serializable> {

    @Autowired
    protected S service;

    @Autowired
    protected M mapper;


    /**
     * 查询过滤属性使用，直接将要过滤的属性设置为Null即可
     *
     * @param object
     * @return
     */
    protected void queryFilter(T object) throws Exception {

    }


    /**
     * 插入需要变换的值，或者校验的值
     *
     * @param object
     * @return
     */
    protected boolean commitFilter(T object) throws Exception {
        return true;
    }


    /**
     * 共有的条件需要在这里配置
     *
     * @param wrapper
     * @return
     */
    protected QueryWrapper queryWrapper(QueryWrapper<T> wrapper) {
        return wrapper;
    }


    /**
     * 返回列表
     *
     * @return
     */
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public ResponseObj<List<T>> list(@ModelAttribute T model) throws Exception {
        List<T> listModel = service.list(queryWrapper(new QueryWrapper<>(model)));
        for (T eachObj : listModel) {
            queryFilter(eachObj);
        }
        return new ResponseObj<>(listModel, RetCode.SUCCESS);
    }

    /**
     * @param page 查询一般传入参数为current和size, 例如/listPage?current=1&size=5,
     * @return 返回分页数据
     */
    @RequestMapping(value = "/page", method = RequestMethod.GET)
    public ResponseObj<Page<T>> listPage(@ModelAttribute Page<T> page, @ModelAttribute T model) throws Exception {

        Page<T> pageList = service.page(page, queryWrapper(new QueryWrapper<>(model)));
        for (T eachObj : pageList.getRecords()) {
            queryFilter(eachObj);
        }
        return new ResponseObj<>(pageList, RetCode.SUCCESS);
    }


    public static Class<?> getMeAndParentFieldType(Class className, String fieldName) {
        for (; className != Object.class; className = className.getSuperclass()) {//获取本身和父级对象
            Field[] fields = className.getDeclaredFields();//获取所有私有字段
            for (Field field : fields) {
                if (field.getName().equals(fieldName)) {
                    return field.getType();
                }
            }
        }
        return null;
    }


    public static QueryWrapper conditionToWrapper(Object model, String conditions) throws Exception {
        QueryWrapper wrapper = new QueryWrapper(model);
        if (conditions == null) {
            return wrapper;
        }

        ObjectMapper objectMapper = ApplicationContextHelper.getBean(ObjectMapper.class);
        SearchCondition[] searchConditions = objectMapper.readValue(conditions, SearchCondition[].class);
        Class<?> tClass = model.getClass();
        for (SearchCondition searchCondition : searchConditions) {
            if (!searchCondition.getType().equals("sorter")) {
                Class<?> fieldType = getMeAndParentFieldType(tClass, searchCondition.getFieldId());//tClass.getDeclaredField(searchCondition.getFieldId()).getType();
                if (fieldType != null && fieldType.getName().equals("java.lang.Long")) {
                    for (int i = 0; i < searchCondition.getValue().length; i++) {
                        long value = Long.parseLong(searchCondition.getValue()[i].toString());
                        searchCondition.getValue()[i] = value;
                    }
                } else if (fieldType != null && fieldType.getName().equals("java.util.Date")) {
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    for (int i = 0; i < searchCondition.getValue().length; i++) {
                        Date value = simpleDateFormat.parse(searchCondition.getValue()[i].toString());
                        searchCondition.getValue()[i] = value;
                    }
                }
            }
            wrapper = doInCondition(searchCondition, wrapper);//是
            wrapper = doInBetween(searchCondition, wrapper);//之间
            wrapper = doOrder(searchCondition, wrapper);
            wrapper = doGe(searchCondition, wrapper);//大于等于
            wrapper = doGt(searchCondition, wrapper);//大于
            wrapper = doLe(searchCondition, wrapper);//小于等于
            wrapper = doLt(searchCondition, wrapper);//小于
            wrapper = doLikeRight(searchCondition, wrapper);
            wrapper = doNotNull(searchCondition, wrapper);
            wrapper = doIsNull(searchCondition, wrapper);
            wrapper = doLike(searchCondition, wrapper);

        }
        return wrapper;
    }

    //大于等于
    public static QueryWrapper doGe(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("ge")) {
            wrapper.ge(CustomMapWrapper.camel2under(searchCondition.getFieldId()), searchCondition.getValue()[0]);
        }
        return wrapper;
    }


    public static QueryWrapper doGt(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("gt")) {
            wrapper.gt(CustomMapWrapper.camel2under(searchCondition.getFieldId()), searchCondition.getValue()[0]);
        }
        return wrapper;
    }


    //小于等于
    public static QueryWrapper doLe(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("le")) {
            wrapper.le(CustomMapWrapper.camel2under(searchCondition.getFieldId()), searchCondition.getValue()[0]);
        }
        return wrapper;
    }


    public static QueryWrapper doLt(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("lt")) {
            wrapper.lt(CustomMapWrapper.camel2under(searchCondition.getFieldId()), searchCondition.getValue()[0]);
        }
        return wrapper;
    }


    public static QueryWrapper doNotNull(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("notNull")) {
            wrapper.isNotNull(CustomMapWrapper.camel2under(searchCondition.getFieldId()));
        }
        return wrapper;
    }

    public static QueryWrapper doIsNull(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("isNull")) {
            wrapper.isNull(CustomMapWrapper.camel2under(searchCondition.getFieldId()));
        }
        return wrapper;
    }

    public static QueryWrapper doLike(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("like")) {
            wrapper.like(CustomMapWrapper.camel2under(searchCondition.getFieldId()), searchCondition.getValue()[0]);
        }
        return wrapper;
    }

    public static QueryWrapper doLikeRight(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("likeRight")) {
            wrapper.likeRight(CustomMapWrapper.camel2under(searchCondition.getFieldId()), searchCondition.getValue()[0]);
        }
        return wrapper;
    }

    public static QueryWrapper doInCondition(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("in")) {
            wrapper.in(CustomMapWrapper.camel2under(searchCondition.getFieldId()), searchCondition.getValue());
        }
        return wrapper;
    }

    public static QueryWrapper doInBetween(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("between")) {
            wrapper.between(CustomMapWrapper.camel2under(searchCondition.getFieldId()), searchCondition.getValue()[0], searchCondition.getValue()[1]);
        }
        return wrapper;
    }


    public static QueryWrapper doOrder(SearchCondition searchCondition, QueryWrapper wrapper) {
        if (searchCondition.getType().equals("sorter")) {
            if (searchCondition.getValue()[0].toString().startsWith("desc")) {
                wrapper.orderByDesc(CustomMapWrapper.camel2under(searchCondition.getFieldId()));
            } else if (searchCondition.getValue()[0].toString().startsWith("asc")) {
                wrapper.orderByAsc(CustomMapWrapper.camel2under(searchCondition.getFieldId()));
            }
        }
        return wrapper;
    }

    @RequestMapping(value = "/listWithSpecialCondition", method = RequestMethod.GET)
    public ResponseObj<List<T>> listWithSpecialCondition(@ModelAttribute T model, String conditions) throws Exception {

        QueryWrapper<T> wrapper = conditionToWrapper(model, conditions);
        List<T> listModel = service.list(queryWrapper(wrapper));
        for (T eachObj : listModel) {
            queryFilter(eachObj);
        }
        return new ResponseObj<>(listModel, RetCode.SUCCESS);
    }

    @RequestMapping(value = "/pageWithSpecialCondition", method = RequestMethod.GET)
    public ResponseObj<Page<T>> pageWithSpecialCondition(@ModelAttribute Page<T> page, @ModelAttribute T model, String conditions) throws Exception {
        QueryWrapper<T> wrapper = conditionToWrapper(model, conditions);
        Page<T> pageList = service.page(page, queryWrapper(wrapper));
        for (T eachObj : pageList.getRecords()) {
            queryFilter(eachObj);
        }
        return new ResponseObj<>(pageList, RetCode.SUCCESS);
    }

    @RequestMapping(value = "/detail", method = RequestMethod.GET)
    public ResponseObj<T> detail(I id) throws Exception {
        T obj = service.getById(id);
        queryFilter(obj);
        return new ResponseObj<>(obj, RetCode.SUCCESS);
    }

    /**
     * insert主要是在insert和update要分开权限的时候使用
     *
     * @param object
     * @return
     */
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public ResponseObj<Boolean> insert(@RequestBody  T object) throws Exception {

        boolean result = commitFilter(object);
        if (result) {
            result = service.save(object);
        }

        RetCode retCode = result ? RetCode.SUCCESS : RetCode.FAIL;
        return new ResponseObj<>(result, retCode);
    }


    /**
     * update主要是在insert和update要分开权限的时候使用
     *
     * @param object
     * @return
     */
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public ResponseObj<Boolean> update(@RequestBody T object) throws Exception {
        boolean result = commitFilter(object);
        if (result) {
            result = service.updateById(object);
        }
        RetCode retCode = result ? RetCode.SUCCESS : RetCode.FAIL;
        return new ResponseObj<>(result, retCode);
    }


    @RequestMapping(value = "/updateWithNull", method = RequestMethod.POST)
    @Transactional(rollbackFor = Exception.class)
    public ResponseObj<Boolean> updateWithNull(@RequestBody Map<String, Object> object) throws Exception {

        ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();


        T o = DeserializeObj(object, (Class) actualTypeArguments[2]);

        ResponseObj<Boolean> update = this.update(o);

        genNullField(object, (Class) actualTypeArguments[2]);

        boolean updateNull = false;

        UpdateWrapper<T> eq = new UpdateWrapper<T>().eq("id", object.get("id"));
        for (String s : object.keySet()) {
            if (object.get(s) == null) {
                updateNull = true;
                eq.set(CustomMapWrapper.camel2under(s), null);
            }
        }
        if (updateNull) {
            service.update(eq);
        }
        return new ResponseObj<>(update.getData(), RetCode.SUCCESS);
    }


    public static <T> T DeserializeObj(Object obj, Class clazz) throws IOException {
        ObjectMapper objectMapper = ApplicationContextHelper.getBean(ObjectMapper.class);
        return (T) objectMapper.readValue(objectMapper.writeValueAsString(obj), clazz);
    }

    public static void genNullField(Map<String, Object> map, Class clazz) {
        List<String> meAndParentFieldName = getMeAndParentFieldName(clazz);

        Arrays.stream(map.keySet().toArray()).forEach(the -> {
            if (meAndParentFieldName.contains(the)) {
                map.put(CustomMapWrapper.camel2under(the.toString()), map.remove(the));
            } else {
                map.remove(the);
            }

        });
    }


    private static List<String> getMeAndParentFieldName(Class className) {
        List<String> fieldList = new ArrayList<>();
        for (; className != Object.class; className = className.getSuperclass()) {//获取本身和父级对象
            Field[] fields = className.getDeclaredFields();//获取所有私有字段
            for (Field field : fields) {
                fieldList.add(field.getName());
            }
        }
        return fieldList;
    }


    /**
     * 根据ID删除数据
     *
     * @param id
     * @return
     */
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public ResponseObj<Boolean> del(I id) throws Exception {
        boolean result = service.removeById(id);
        RetCode retCode = result ? RetCode.SUCCESS : RetCode.FAIL;
        return new ResponseObj<>(result, retCode);
    }


    /**
     * 根据id批量删除
     */
    @RequestMapping(value = "/deleteBatch", method = RequestMethod.POST)
    public ResponseObj<Boolean> delList(@RequestBody List<I> ids) throws Exception {
        boolean result = service.removeByIds(ids);
        RetCode retCode = result ? RetCode.SUCCESS : RetCode.FAIL;
        return new ResponseObj<>(result, retCode);
    }


    /**
     * 批量新增
     */
    @RequestMapping(value = "/insertBatch", method = RequestMethod.POST)
    public ResponseObj<Boolean> insertBatch(@RequestBody Collection<T> entityList) {
        boolean result = service.saveBatch(entityList);
        RetCode retCode = result ? RetCode.SUCCESS : RetCode.FAIL;
        return new ResponseObj<>(result, retCode);
    }


    @RequestMapping(value = "/insertBatchWithReturn", method = RequestMethod.POST)
    public ResponseObj<Collection<T>> insertBatchWithReturn(@RequestBody Collection<T> entityList) {
        boolean result = service.saveBatch(entityList);
        RetCode retCode = result ? RetCode.SUCCESS : RetCode.FAIL;

        return new ResponseObj<>(entityList, retCode);
    }


    @RequestMapping(value = "/delInsert", method = RequestMethod.POST)
    @Transactional(rollbackFor = Exception.class)
    public ResponseObj<Boolean> delInsert(@RequestBody DelInsertModel<T> delInsertModel) {
        service.remove(new QueryWrapper<T>().eq(CustomMapWrapper.camel2under(delInsertModel.getDelColumn()), delInsertModel.getDelId()));
        service.saveBatch(delInsertModel.getList());
        return new ResponseObj<>(true, RetCode.SUCCESS);
    }


    @RequestMapping(value = "/insertWithReturn", method = RequestMethod.POST)
    public ResponseObj<T> insertWithReturn(@RequestBody T object) throws Exception {
        boolean result = commitFilter(object);
        if (result) {
            result = service.save(object);
        }
        RetCode retCode = result ? RetCode.SUCCESS : RetCode.FAIL;
        return new ResponseObj<>(object, retCode);
    }


}
